Software programs and systems, generally referred to as software applications or applications, often perform mission critical tasks in various fields such as monitoring and control of medical systems, financial transactions, monitoring and control of industrial manufacturing, etc. An application performing a critical task fails sometimes, e.g., due to a software error. Via code analysis, testing, and repairs to the code, software errors can be avoided or at least minimized. Sometimes, however, the execution of a software application fails not due to a software error but due to an event in the environment in which the application is executed. For example, a disk or another memory from which the application accesses data may fail, a network link for accessing one or more components of the application and/or required data may fail, etc. Such environmental failures can cause a software application to fail.
In various fields, such as those described above, a software application is expected not only to deliver high performance, e.g., to generate required computational results as quickly as possible, but also to provide these results in a reliable manner. While software errors can be avoided or at least minimized, as described above, errors caused by environmental factors and/or hardware failures tend to be unpredictable. One way to improve reliability of the software application, therefore, is to execute simultaneously two instances of the software application. Even if one instance fails due to an environmental event, it is not very likely that at or about that moment of time an event would occur in the environment of the other instance, causing that instance to fail, as well. Therefore, the other instance, often called as a backup instance, may continue to perform the required computational tasks.
This redundancy-based approach presents some challenges, however. First, running two instances of the software application generally increases the cost of executing the software application in terms of required resources such as processors, servers, memory, networking interfaces, etc. Second, this approach is not highly scalable because another environmental event that causes a failure of the backup instance may occur. While two or more backup instances can be executed simultaneously, to increase redundancy and thus reliability further, this can also increase the cost of executing the software application even further.
For efficient execution of a software application, interpreted computer languages offer several benefits over their compiled counterparts, such as greater portability across different computer architectures. Running code in an interpreter can be dramatically slower, however, than running the same code compiled to a processor's native machine language. To overcome this disadvantage, many interpreters can improve performance by including just-in-time (JIT) compilation, in which at least a portion of the interpreter's bytecode is compiled to the processor's native code. The interpreter can then execute the native code instead of interpreting the bytecode for that portion of the program. This feature is provided in several interpreter runtimes, including the standard HotSpot implementation of the Java Virtual Machine (JVM) and Microsoft's Common Language Runtime (commonly known as .NET), and may offer performance comparable to traditional ahead-of-time (AIT) compiled languages.
Another performance improvement is possible with JIT compiled languages, where as the interpreter executes a particular bytecode (or another intermediate representation, or source code), the interpreter can gather profiling information about the executed code or at least a portion thereof, that enables a greater degree of optimization when the code or a portion thereof is compiled. This adaptive optimization is provided in the HotSpot JVM and can sometimes offer better performance than ahead-of-time compiled code. One major drawback of this technique is that the code or a portion thereof must be run many times (e.g., tens, hundreds, thousands, or even more times) using the interpreter to collect sufficient profiling information to optimize the compilation. During the profiling phase, the software application typically runs slower than a compiled version compiled using traditional compilation technique such as AIT compilation. Only after profiling of the code is completed the adaptive JIT may offer enhanced performance.